package com.zhuliwei.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zhuliwei.constants.RabbitConstants;
import com.zhuliwei.constants.SystemConstants;
import com.zhuliwei.domain.ResponseResult;
import com.zhuliwei.domain.dto.*;
import com.zhuliwei.domain.entity.Notice;
import com.zhuliwei.domain.entity.Role;
import com.zhuliwei.domain.entity.User;
import com.zhuliwei.domain.entity.UserRole;
import com.zhuliwei.domain.vo.*;
import com.zhuliwei.enums.AppHttpCodeEnum;
import com.zhuliwei.exception.SystemException;
import com.zhuliwei.mapper.UserMapper;
import com.zhuliwei.patterns.SystemPatterns;
import com.zhuliwei.service.NoticeService;
import com.zhuliwei.service.RoleService;
import com.zhuliwei.service.UserRoleService;
import com.zhuliwei.service.UserService;
import com.zhuliwei.utils.*;
import jakarta.annotation.Resource;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.zhuliwei.constants.RabbitConstants.EMAIL_EXCHANGE_NAME;
import static com.zhuliwei.constants.RabbitConstants.EMAIL_MESSAGE_KEY;

/**
 * 用户表(User)表服务实现类
 *
 * @author 朱力维
 * @since 2022-10-14 15:15:51
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    @Resource
    PasswordEncoder passwordEncoder;
    @Resource
    private RedisCache redisCache;

    @Resource
    private UserRoleService userRoleService;

    @Resource
    private RoleService roleService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private NoticeService noticeService;

    @Override
    public ResponseResult userInfo() {
        // 获取登录用户id
        String userId = SecurityUtils.getUserId();
        User user = getById(userId);
        UserInfoVo userInfoVo = BeanCopyUtils.copyBean(user, UserInfoVo.class);
        return ResponseResult.okResult(userInfoVo);
    }

    @Override
    public ResponseResult updateUserInfo(User user) {
        // 如果前端用户修改了昵称就判断数据库中是否有相同的
//        if (!SecurityUtils.getNickName().equals(user.getNickName())) {
//            if (nickNameExist(user.getNickName())) {
//                throw new SystemException(AppHttpCodeEnum.NICKNAME_EXIST);
//            }
//        }
        if (!Objects.isNull(user.getSignature()) && 30 < user.getSignature().length()) {
            throw new SystemException(AppHttpCodeEnum.SIGNATURE_LENGTH_LIMIT);
        }
        updateById(user);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult register(User user) {
        //对数据进行非空判断
        if (!StringUtils.hasText(user.getAuthCode())) {
            throw new SystemException(AppHttpCodeEnum.AUTHCODE_NOT_NULL);
        }
        if (!StringUtils.hasText(user.getUserName())) {
            throw new SystemException(AppHttpCodeEnum.USERNAME_NOT_NULL);
        }
        if (!StringUtils.hasText(user.getPassword())) {
            throw new SystemException(AppHttpCodeEnum.PASSWORD_NOT_NULL);
        }
        if (!StringUtils.hasText(user.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
        }
        if (!StringUtils.hasText(user.getNickName())) {
            throw new SystemException(AppHttpCodeEnum.NICKNAME_NOT_NULL);
        }
        //对数据进行是否存在的判断
        if (userNameExist(user.getUserName())) {
            throw new SystemException(AppHttpCodeEnum.USERNAME_EXIST);
        }
        if (nickNameExist(user.getNickName())) {
            throw new SystemException(AppHttpCodeEnum.NICKNAME_EXIST);
        }
        if (emailExist(user.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_EXIST);
        }
        //判断邮箱验证码是否正确
        String authCode = redisCache.getCacheObject(SystemConstants.EMAIL_CODE_PREFIX + user.getEmail());
        if (!user.getAuthCode().equals(authCode)) {
            throw new SystemException(AppHttpCodeEnum.AUTHCODE_ERROR);
        }
        // 判断密码格式是否正确
        AssertUtil.checkFalse(user.getPassword().matches(SystemPatterns.PASSWORD_CHECK), "输入密码格式错误");
        //判断两次输入的密码是否一致
        if (!user.getPassword().equals(user.getRpassword())) {
            throw new SystemException(AppHttpCodeEnum.RPASSWORD_ERROR);
        }
        //对密码进行加密
        String encodePassword = passwordEncoder.encode(user.getPassword());
        user.setPassword(encodePassword);
        //存入数据库
        save(user);
        //注册成功删除验证码防止重复利用
        redisCache.deleteObject(SystemConstants.EMAIL_CODE_PREFIX + user.getEmail());
        Map<String, Object> userMessage = new HashMap<>();
        userMessage.put("to", user.getEmail());
        userMessage.put("nickName", user.getNickName());
        String corrId = RabbitConstants.EMAIL_MESSAGE_PREMIX + IdUtils.getRandomUUID();
        // 发布确认：将要发送的消息备份到redis
        redisCache.setCacheMapValue(RabbitConstants.RABBIT_RENEW, corrId, userMessage);
        //注册完成通过消息队列通知邮箱接口发送注册成功邮件 实现异步操作
        rabbitTemplate.convertAndSend(EMAIL_EXCHANGE_NAME, EMAIL_MESSAGE_KEY, userMessage, new CorrelationData(corrId));
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult<PageVo> userPageList(Integer pageNum, Integer pageSize, UserDto userDto) {
        UserMapper userMapper = getBaseMapper();
//        List<User> users = userMapper.pageList(pageNum, pageSize, userDto);
        Long total = userMapper.selectCount(null);
        //分页查询
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper();
        lqw.like(StringUtils.hasText(userDto.getUserName()), User::getUserName, userDto.getUserName());
        lqw.eq(StringUtils.hasText(userDto.getStatus()), User::getStatus, userDto.getStatus());
        lqw.eq(StringUtils.hasText(userDto.getEmail()), User::getEmail, userDto.getEmail());
        lqw.orderByDesc(User::getCreateTime);
        lqw.eq(StringUtils.hasText(userDto.getType()), User::getType, userDto.getType());
        Page<User> page = new Page<>();
        page.setCurrent(pageNum);
        page.setSize(pageSize);
        page(page, lqw);
        List<User> users = page.getRecords();
        List<UserListVo> userListVos = BeanCopyUtils.copyBeanList(users, UserListVo.class);
        //封装数据返回
//        PageVo pageVo = new PageVo(userListVos, total);
        PageVo pageVo = new PageVo(userListVos, page.getTotal());
        return ResponseResult.okResult(pageVo);
    }

    @Override
    @Transactional
    public ResponseResult add(UserAddDto userAddDto) {
        if (!CollectionUtils.isEmpty(userAddDto.getRoleIds())) {
            if (userAddDto.getRoleIds().contains(SystemConstants.TYPE_ADMIN)) {
                throw new SystemException(AppHttpCodeEnum.SUPER_ADMIN_LIMIT);
            }
        }
//        if (!StringUtils.hasText(userAddDto.getEmail())) {
//            throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
//        }
        if (!StringUtils.hasText(userAddDto.getUserName())) {
            throw new SystemException(AppHttpCodeEnum.USERNAME_NOT_NULL);
        }
        if (!StringUtils.hasText(userAddDto.getPassword())) {
            throw new SystemException(AppHttpCodeEnum.PASSWORD_NOT_NULL);
        }
        if (emailExist(userAddDto.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_EXIST);
        }
        if (userNameExist(userAddDto.getUserName())) {
            throw new SystemException(AppHttpCodeEnum.USERNAME_EXIST);
        }
        if (phonenumberExist(userAddDto.getPhonenumber())) {
            throw new SystemException(AppHttpCodeEnum.PHONENUMBER_EXIST);
        }
        String passwordEncode = passwordEncoder.encode(userAddDto.getPassword());
        String uuid = UUID.randomUUID().toString().replaceAll("-", "");
        User user = BeanCopyUtils.copyBean(userAddDto, User.class);
        user.setId(uuid);
        user.setPassword(passwordEncode);
        if (!CollectionUtils.isEmpty(userAddDto.getRoleIds())) {
            // 如果添加了角色则为管理员 没有则为普通用户
            user.setType("1");
            List<UserRole> userRoles = userAddDto.getRoleIds().stream().map(
                    roleId -> new UserRole(uuid, roleId)
            ).collect(Collectors.toList());
            userRoleService.saveBatch(userRoles);
        }
        save(user);
        return ResponseResult.okResult();
    }

    @Override
    @Transactional
    public ResponseResult delete(List<String> ids) {
        if (ids.contains(SecurityUtils.getUserId())) {
            throw new SystemException(AppHttpCodeEnum.DELETE_LOGIN_USER);
        }
        if (!SecurityUtils.isSuperAdmin()) {
            // 普通管理员无法删除其他管理员
            for (String id :
                    ids) {
                LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
                lqw.select(User::getType);
                lqw.eq(User::getId, id);
                User one = getOne(lqw);
                if (one.getType().equals(SystemConstants.TYPE_ADMIN)) {
                    throw new SystemException(AppHttpCodeEnum.NO_POWER);
                }
            }
        }
        removeByIds(ids);
        for (String id : ids
        ) {
            LambdaQueryWrapper<UserRole> lqw = new LambdaQueryWrapper<>();
            lqw.eq(UserRole::getUserId, id);
            userRoleService.remove(lqw);
            // 删除通知表
            LambdaQueryWrapper<Notice> lqwN = new LambdaQueryWrapper<>();
            lqwN.eq(Notice::getArticleId, id);
            noticeService.remove(lqwN);
        }
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult detail(String id) {
        User user = getById(id);
        if (!SecurityUtils.isSuperAdmin()) {
            if (user.getType().equals(SystemConstants.TYPE_ADMIN)) {
                throw new SystemException(AppHttpCodeEnum.NO_POWER);
            }
        }
        UserListVo userListVo = BeanCopyUtils.copyBean(user, UserListVo.class);
        LambdaQueryWrapper<Role> roleLqw = new LambdaQueryWrapper<>();
        roleLqw.eq(Role::getStatus, SystemConstants.STATUS_NORMAL);
        List<Role> roles = roleService.list(roleLqw);
//        roles = roles.stream()
//                .filter(role -> !role.getId().equals(SystemConstants.TYPE_ADMIN))
//                .collect(Collectors.toList());
        List<RoleUpdateVo> roleVos = BeanCopyUtils.copyBeanList(roles, RoleUpdateVo.class);
        LambdaQueryWrapper<UserRole> userRoleLqw = new LambdaQueryWrapper<>();
        userRoleLqw.eq(UserRole::getUserId, id);
        List<String> roleIds = userRoleService.list(userRoleLqw).stream()
                .map(userRole -> userRole.getRoleId())
                .collect(Collectors.toList());
//        if (user.getId().equals("1535f63ffb3748dba254483725ddda1c")){
//            return ResponseResult.okResult(new UserDetailVo(roleIds,null,userListVo));
//        }
        return ResponseResult.okResult(new UserDetailVo(roleIds, roleVos, userListVo));
    }

    @Override
    public ResponseResult forgetPassword(UserForgetDto userDto) {
        if (!StringUtils.hasText(userDto.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_NULL);
        }
        if (!StringUtils.hasText(userDto.getAuthCode())) {
            throw new SystemException(AppHttpCodeEnum.AUTHCODE_NOT_NULL);
        }
        if (!StringUtils.hasText(userDto.getNewPassword())) {
            throw new SystemException(AppHttpCodeEnum.NEWPWD_NOT_NULL);
        }
        if (emailNotExist(userDto.getEmail())) {
            throw new SystemException(AppHttpCodeEnum.EMAIL_NOT_EXIST);
        }
        //判断邮箱验证码是否正确
        String authCode = redisCache.getCacheObject(SystemConstants.EMAIL_CODE_PREFIX + userDto.getEmail());
        if (!userDto.getAuthCode().equals(authCode)) {
            throw new SystemException(AppHttpCodeEnum.AUTHCODE_ERROR);
        }
        // 判断密码格式是否正确
        AssertUtil.checkFalse(userDto.getNewPassword().matches(SystemPatterns.PASSWORD_CHECK), "输入密码格式错误");
        //对密码进行加密
        String encodePassword = passwordEncoder.encode(userDto.getNewPassword());
        //修改密码
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getEmail, userDto.getEmail());
        User user = getOne(lqw);
        user.setPassword(encodePassword);
        update(user, lqw);
        //修改密码成功删除验证码防止重复利用
        redisCache.deleteObject(SystemConstants.EMAIL_CODE_PREFIX + userDto.getEmail());
        return ResponseResult.okResult();
    }

    @Override
    @Transactional
    public ResponseResult updateUser(UserUpdateDto userDto) {
        // 普通管理员没有权限修改超级管理员
        if (!SecurityUtils.isSuperAdmin()
                && "1535f63ffb3748dba254483725ddda1c".equals(userDto.getId())) {
            throw new SystemException(AppHttpCodeEnum.NO_POWER_UPDATE);
        }
        // 超级管理员不可停用
        if ("1535f63ffb3748dba254483725ddda1c".equals(userDto.getId())
                && !userDto.getStatus().equals(SystemConstants.STATUS_NORMAL)) {
            throw new SystemException(AppHttpCodeEnum.ADMIN_STOP_ERROR);
        }
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        User user = getById(userDto.getId());
        // 邮箱不能和其他用户一样
        if (!userDto.getEmail().equals(user.getEmail())) {
            lqw.eq(User::getEmail, userDto.getEmail());
            if (count(lqw) > 0) {
                throw new SystemException(AppHttpCodeEnum.EMAIL_EXIST);
            }
        }
        // 只能有一个超级管理员
        if (userDto.getRoleIds().contains(SystemConstants.TYPE_ADMIN)
                && !user.getType().equals(SystemConstants.TYPE_ADMIN)) {
            throw new SystemException(AppHttpCodeEnum.SUPER_ADMIN_LIMIT);
        }
        // 普通用户不能分配权限
        if (userDto.getType().equals(SystemConstants.TYPE_USER) &&
                !CollectionUtils.isEmpty(userDto.getRoleIds())) {
            throw new SystemException(AppHttpCodeEnum.USER_NO_POWER);
        }
        if (!CollectionUtils.isEmpty(userDto.getRoleIds())
                && userDto.getType().equals(SystemConstants.TYPE_ADMIN)) {
            LambdaQueryWrapper<UserRole> userRoleLqw = new LambdaQueryWrapper();
            userRoleLqw.eq(UserRole::getUserId, userDto.getId());
            userRoleService.remove(userRoleLqw);
            List<UserRole> userRoles = userDto.getRoleIds().stream()
                    .map(roleId -> {
                        return new UserRole(userDto.getId(), roleId);
                    })
                    .collect(Collectors.toList());
            userRoleService.saveBatch(userRoles);
        }
        user.setNickName(userDto.getNickName());
        user.setPhonenumber(userDto.getPhonenumber());
        user.setEmail(userDto.getEmail());
        user.setSex(userDto.getSex());
        user.setStatus(userDto.getStatus());
        updateById(user);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult updatePassword(UpdatePasswordDto userDto) {
        AssertUtil.checkBlank(userDto.getPassword(), "新密码不能为空");
        AssertUtil.checkBlank(userDto.getRePassword(), "重复密码不能为空");
        // 判断密码格式是否正确
        AssertUtil.checkFalse(userDto.getPassword().matches(SystemPatterns.PASSWORD_CHECK), "输入密码格式错误");
        AssertUtil.checkFalse(userDto.getPassword().equals(userDto.getRePassword()), "两次输入密码不一致");
        userDto.setId(SecurityUtils.getUserId());
        //对密码进行加密
        userDto.setPassword(passwordEncoder.encode(userDto.getPassword()));
        User user = BeanCopyUtils.copyBean(userDto, User.class);
        updateById(user);
        return ResponseResult.okResult();
    }

    @Override
    public User findByOpenId(String openId) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getOpenId, openId);
        return getOne(lqw);
    }

    private boolean emailExist(String email) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getEmail, email);
        return count(lqw) > 0;
    }

    private boolean emailNotExist(String email) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getEmail, email);
        return count(lqw) <= 0;
    }

    private boolean nickNameExist(String nickName) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getNickName, nickName);
        return count(lqw) > 0;
    }

    private boolean userNameExist(String userName) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getUserName, userName);
        return count(lqw) > 0;
    }

    private boolean phonenumberExist(String phonenumber) {
        LambdaQueryWrapper<User> lqw = new LambdaQueryWrapper<>();
        lqw.eq(User::getPhonenumber, phonenumber);
        return count(lqw) > 0;
    }
}

